package com.hooper.service.impl;

import cn.hutool.json.JSONUtil;
import com.hooper.exception.BusinessException;
import com.hooper.mapper.SysMenuMapper;
import com.hooper.mapper.SysRoleMenuMapper;
import com.hooper.model.dto.SysMenuDto;
import com.hooper.model.dto.UserRoleDto;
import com.hooper.model.system.SysMenu;
import com.hooper.model.vo.AssignMenuVo;
import com.hooper.service.SysMenuService;
import com.hooper.service.UserRoleService;
import com.hooper.util.MenuHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.hooper.constant.SysMenuConstant.*;
import static com.hooper.constant.UserConstant.ADMIN_ID;
import static com.hooper.emun.ResultCodeEnum.NULL_PARAM;

/**
 * @author Tim Hooper
 * @version 1.0
 * @time 2023/04/01/21:01
 */
@Service
@Slf4j
public class SysMenuServiceImpl implements SysMenuService {

    @Resource
    private SysMenuMapper menuMapper;
    @Resource
    private SysRoleMenuMapper roleMenuMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private UserRoleService userRoleService;


    @Override
    public List<SysMenuDto> getAllMenus() {
        String jsonResult = stringRedisTemplate.opsForValue().get(MENU_CACHE_KEY);
        if (!StringUtils.isEmpty(jsonResult)) {
            return JSONUtil.toList(jsonResult, SysMenuDto.class);
        }
        List<SysMenuDto> topMenus = menuMapper.selectMenusByParentId(TOP_MENU_ID);
        List<SysMenuDto> allMenus = new ArrayList<>();
        for (SysMenuDto topMenu : topMenus) {
            addSubMenu(topMenu);
            allMenus.add(topMenu);
        }
        jsonResult = JSONUtil.toJsonStr(allMenus);
        stringRedisTemplate.opsForValue().set(MENU_CACHE_KEY, jsonResult, CACHE_EXPIRE_TIME, TimeUnit.MINUTES);
        return allMenus;
    }

    @Override
    public void saveMenu(SysMenu menu) {
        if (menu == null) {
            throw new BusinessException(NULL_PARAM, "菜单不能为空");
        }
        // 新增菜单后，清除菜单缓存
        stringRedisTemplate.delete(MENU_CACHE_KEY);
        menuMapper.insert(menu);
    }

    @Override
    public void updateMenuById(SysMenu newMenu) {
        if (newMenu == null) {
            throw new BusinessException(NULL_PARAM, "菜单不能为空");
        }
        // 更新菜单后，清除菜单缓存
        stringRedisTemplate.delete(MENU_CACHE_KEY);
        menuMapper.updateById(newMenu);
    }


    @Override
    public void delMenuById(Long id) {
        if (id == null) {
            throw new BusinessException(NULL_PARAM, "菜单id不能为空");
        }
        List<SysMenuDto> children = menuMapper.selectMenusByParentId(id);
        if (!CollectionUtils.isEmpty(children)) {
            throw new BusinessException("请先删除子菜单");
        }
        // 删除菜单后，清除菜单缓存
        stringRedisTemplate.delete(MENU_CACHE_KEY);
        menuMapper.deleteById(id);
    }

    @Override
    public List<SysMenuDto> getMenusByRoleId(Long roleId) {
        if (roleId == null) {
            throw new BusinessException(NULL_PARAM, "角色id不能为空");
        }
        // 获取所有菜单
        List<SysMenuDto> menuDtoList = menuMapper.selectAllValidate();
        // 获取角色已分配的菜单id
        Set<Long> menuIds = roleMenuMapper.selectMenuIdsByRoleId(roleId);
        // 若该角色没有分配菜单，则直接返回所有菜单并设为未选中
        if (CollectionUtils.isEmpty(menuIds)) {
            return MenuHelper.toTree(menuDtoList);
        }
        // 若该角色已分配菜单，则将已分配的菜单设为选中
        menuDtoList.forEach(menu -> menu.setSelect(menuIds.contains(menu.getId())));
        return MenuHelper.toTree(menuDtoList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assignMenu2Role(AssignMenuVo assignMenuVo) {
        //先删除旧角色菜单关联表的数据
        if (assignMenuVo == null || assignMenuVo.getRoleId() == null) {
            throw new BusinessException(NULL_PARAM);
        }
        Long roleId = assignMenuVo.getRoleId();
        roleMenuMapper.deleteMenuByRoleId(roleId);
        //如果没有菜单id，直接返回
        if (CollectionUtils.isEmpty(assignMenuVo.getMenuIdList())) {
            return;
        }
        //插入新的角色菜单关联表的数据
        roleMenuMapper.insertRoleMenu(roleId, assignMenuVo.getMenuIdList());
    }

    @Override
    public List<String> selectButtonsByUserId(Long userId) {
        List<SysMenuDto> menuList = initMenuList(userId);
        ;
        // 获取按钮列表
        return menuList.stream()
                .filter(menu -> menu.getType().equals(MENU_TYPE_BUTTON))
                .map(SysMenuDto::getPerms).collect(Collectors.toList());
    }

    @Override
    public List<SysMenuDto> initMenuList(Long userId) {
        List<SysMenuDto> menuList;
        if (Objects.equals(userId, ADMIN_ID)) {
            menuList = menuMapper.selectAllValidate();
        } else {
            // 根据用户id 获取该用户可操作的菜单列表
            UserRoleDto userRole = userRoleService.getUserRolesById(userId);
            List<Long> roles = userRole.getRoles();
            menuList = menuMapper.selectMenusByRoles(roles);
        }
        return menuList;
    }


    private void addSubMenu(SysMenuDto topMenu) {
        Long id = topMenu.getId();
        List<SysMenuDto> subMenus = menuMapper.selectMenusByParentId(id);
        if (!CollectionUtils.isEmpty(subMenus)) {
            topMenu.setChildren(subMenus);
            for (SysMenuDto subMenu : subMenus) {
                addSubMenu(subMenu);
            }
        } else {
            topMenu.setChildren(Collections.emptyList());
        }
    }
}
